package com.yuncheng.system.userlock.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yuncheng.system.orguser.entity.SysUser;
import com.yuncheng.system.orguser.service.ISysUserService;
import com.yuncheng.system.userlock.service.SysUserLockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;

/**
 * 用户锁定表服务
 */
@Service
public class SysUserLockServiceImpl implements SysUserLockService {

    /**
     * 是否用户已被冻结 1(解冻)正常 2冻结
     */
    public static final Integer USER_NORMAL = 1;
    public static final Integer USER_FREEZE = 2;

    private static final String FAILCOUNT_KEY = "fc";
    private static final String UNLOCKTIME_KEY = "ut";
    private static final String OLDDATA_KEY = "o";

    private static final int MAX_FIELD_LEN = 450;

    @Value("${login_password_error_maxcount:5}")
    private Integer passwordErrorMaxCount;

    @Autowired
    private ISysUserService sysUserService;

    private String saveDataToUserExt6(SysUser sysUser, JSONObject userLockData) {
        SysUser updateUser = new SysUser();
        updateUser.setId(sysUser.getId());
        String result = (null != userLockData && userLockData.size() > 0) ? userLockData.toString() : "";
        updateUser.setUserExt6(result);
        sysUserService.updateById(updateUser);
        return result;
    }

    private void changeUserStatus(SysUser sysUser, Integer status) {
        SysUser updateUser = new SysUser();
        updateUser.setId(sysUser.getId());
        updateUser.setStatus(status);
        sysUserService.updateById(updateUser);
    }

    private int getFailCount(JSONObject obj) {
        try {
            return Integer.parseInt(String.valueOf(obj.get(FAILCOUNT_KEY)), 10);
        } catch (Throwable e) {
            return 1;
        }
    }

    private Date getUnlockTime(JSONObject obj) {
        try {
            Object t = obj.get(UNLOCKTIME_KEY);
            if (null != t) {
                if (t instanceof Number) {
                    return DateUtil.date(((Number) t).longValue());
                } else if (t instanceof String) {
                    return DateUtil.parse((String) t);
                }
            }
        } catch (Throwable e) {
        }
        return null;
    }

    private JSONObject toJson(String userLockData) {
        JSONObject obj = null;
        try {
            obj = JSONUtil.parseObj(userLockData);
        } catch (Throwable e) {
            obj = new JSONObject();
            if (StringUtils.hasText(userLockData)) {
                if (userLockData.length() > MAX_FIELD_LEN) {
                    userLockData = userLockData.substring(0, MAX_FIELD_LEN);
                }
                obj.put(OLDDATA_KEY, userLockData);
            }
        }
        if (null == obj) {
            obj = new JSONObject();
        }
        return obj;
    }

    @Override
    public int passwordError(SysUser user) {
        //剩余次数
        int result = passwordErrorMaxCount;

        if (null != user) {
            String userLockData = user.getUserExt6();
            JSONObject obj = toJson(userLockData);

            if (null == obj || obj.size() == 0) {
                obj = new JSONObject();
                obj.put(FAILCOUNT_KEY, 1);
                user.setUserExt6(saveDataToUserExt6(user, obj));
            } else {
                boolean change = false;
                int nowfc = getFailCount(obj) + 1;
                if (nowfc <= passwordErrorMaxCount) {
                    obj.put(FAILCOUNT_KEY, nowfc);
                    change = true;
                }
                if (nowfc >= passwordErrorMaxCount && !USER_FREEZE.equals(user.getStatus())) {
                    //锁定到明天0点
                    obj.put(UNLOCKTIME_KEY, DateUtil.beginOfDay(DateUtil.tomorrow()).toString());

                    //设置用户为锁定状态
                    changeUserStatus(user, USER_FREEZE);
                    change = true;
                }
                if (change) {
                    user.setUserExt6(saveDataToUserExt6(user, obj));
                }
            }
            result = passwordErrorMaxCount - getFailCount(obj);
            if (passwordErrorMaxCount < 0) {
                passwordErrorMaxCount = 0;
            }
        } else {
            result = 0;
        }
        return result;
    }

    @Override
    public void passwordSuccess(SysUser user) {
        if (null != user && null != user.getUserExt6()) {
            String userLockData = user.getUserExt6();
            JSONObject obj = toJson(userLockData);
            if (null != obj) {
                obj.remove(FAILCOUNT_KEY);
                obj.remove(UNLOCKTIME_KEY);
            }
            user.setUserExt6(saveDataToUserExt6(user, obj));
        }
    }

    @Override
    public boolean isUserLocked(SysUser user) {
        if (null != user) {
            String userLockData = user.getUserExt6();
            JSONObject obj = toJson(userLockData);

            if (obj.size() > 0) {
                Date ut = getUnlockTime(obj);
                if (null != ut) {
                    if (USER_FREEZE.equals(user.getStatus()) && DateUtil.current() < ut.getTime()) {
                        // 还在锁定时间段内
                        return true;
                    } else {
                        // 已过锁定时间, 去掉用户锁定数据
                        if (null != obj) {
                            obj.remove(FAILCOUNT_KEY);
                            obj.remove(UNLOCKTIME_KEY);
                        }
                        user.setUserExt6(saveDataToUserExt6(user, obj));

                        //设置用户为正常状态
                        changeUserStatus(user, USER_NORMAL);
                        user.setStatus(USER_NORMAL);

                    }
                }
            } else if (USER_FREEZE.equals(user.getStatus())) {
                // 用户账号被人工冻结
                return true;
            }
        }
        return false;
    }

}
